package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.*;
import gov.nasa.anml.PDDL.Argument;
import gov.nasa.anml.PDDL.FunctionReference;
import gov.nasa.anml.PDDL.PredicateReference;
import gov.nasa.anml.PDDL.Time;
import gov.nasa.anml.utility.*;

// really this is a function binding

public class ConstantFunctionReference<T extends SimpleObject<T>> extends Bind<ConstantFunction<T>,T,SimpleVoid> implements ConstantExpression<T> {
	
	public ConstantFunctionReference() {}

	public ConstantFunctionReference(ConstantFunction<T> ref) {
		super(ref);
	}
	
	// could make a pool of various arity arrays to avoid garbage collection
	// and memory bloat simultaneously	
	public T value(State s) {
		ConstantExpression<T> init = ref.init.get(setArgs(s));
		return init == null ? null : init.value(s);
	}

	public TypeCode typeCode() {
		return ref.typeCode();
	}

	public History<SimpleVoid> storage(State p, State c) {
		return null;
	}

	public boolean apply(State p, int contextID, State c) {
		if (ref.typeCode() != TypeCode.Boolean)
			return false;
		if (value(p) != ANMLBoolean.True)
			return false;
		return true;
	}


	public transient PDDL.PredicateReference asPDDLPredicateReference;
	public transient PDDL.FunctionReference asPDDLFunctionReference;
	public transient PDDL.Expression asPDDLExpression;

	public PDDL.Expression translateExpr(PDDL pddl, Interval unit) {
		if (asPDDLExpression != null)
			return asPDDLExpression;
		switch(typeCode()) {
		case Float:
			return asPDDLExpression = translateRefF(pddl,unit);
		case Boolean:
			return asPDDLExpression = translateRefB(pddl,unit);
		default:
			System.err.println("Oh no! PDDL only supports fluent booleans and floats (symbols/objects are okay as arguments/parameters, but not as values).");
		}
		return pddl.FalseRef;
	}
	
	public PDDL.Expression translateLValue(PDDL pddl, Interval unit) {
		if (asPDDLExpression != null)
			return asPDDLExpression;
		switch(typeCode()) {
		case Float:
			return asPDDLExpression = translateRefF(pddl,unit);
		case Boolean:
			return asPDDLExpression = translateRefB(pddl,unit);
		default:
			System.err.println("Oops!");
		}
		return super.translateLValue(pddl,unit);
	}

	PDDL.PredicateReference translateRefB(PDDL pddl, Interval unit) {
		if (asPDDLPredicateReference != null)
			return asPDDLPredicateReference;
		asPDDLPredicateReference = pddl.new PredicateReference(ref.boolPDDL);
		for (Expression<? extends SimpleObject<?>,?> e : this.arguments) {
			PDDL.Argument a = e.translateArgument(pddl,unit);
			if (a != null)
				asPDDLPredicateReference.arguments.add(a);
			else {
				System.err.println("Arguments must be object/symbol literals or parameters.");
			}
		}		
		return asPDDLPredicateReference;
	}
	
	PDDL.FunctionReference translateRefF(PDDL pddl, Interval unit) {
		if (asPDDLFunctionReference == null)
			return asPDDLFunctionReference;
		asPDDLFunctionReference = pddl.new FunctionReference(ref.floatPDDL);
		for (Expression<? extends SimpleObject<?>,?> e : this.arguments) {
			PDDL.Argument a = e.translateArgument(pddl,unit);
			if (a != null)
				asPDDLFunctionReference.arguments.add(a);
			else {
				System.err.println("Arguments must be object/symbol literals or parameters.");
			}
		}		
		return asPDDLFunctionReference;
	}
	


}

